/**
**************************************************************************************
*Palisma - Secrets of the Illuminati is an open-source 2D RPG                        *
*Copyright (C) 2006, Tony Sparks                                                     *
*                                                                                    *
*This library is free software; you can redistribute it and/or                       *
*modify it under the terms of the GNU Lesser General Public                          *
*License as published by the Free Software Foundation; either                        *
*version 2.1 of the License, or (at your option) any later version.                  *
*                                                                                    *
*This library is distributed in the hope that it will be useful,                     *
*but WITHOUT ANY WARRANTY; without even the implied warranty of                      *
*MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU                   *
*Lesser General Public License for more details.                                     *
*                                                                                    *
*You should have received a copy of the GNU Lesser General Public                    *
*License along with this library; if not, write to the Free Software                 *
*Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA      *
**************************************************************************************
*/
#include "StdAfx.h"
#include "EntityFactory.h"
#include "StateFactory.h"

#include "FloorSwitch.h"
#include "Door.h"
#include "Shotgun.h"
#include "Melee.h"

#include "../shared/MFile.h"
#include "../shared/StringUtil.h"
#include "../shared/geom.h"
#include "../render/ViewManager.h"
#include "../render/EntityView.h"
#include "../render/ViewManager.h"
#include "../render/Model.h"

#include "../kernel.h"

extern Kernel* g_kernel;

EntityFactory* EntityFactory::instance = new EntityFactory;

EntityFactory::EntityFactory(void)
{
    m_playerCreated = false;
    m_player = NULL;
}

EntityFactory::~EntityFactory(void)
{
}


/** Create an entity */
IEntity* EntityFactory::CreateEntity( const std::string &entName )
{
    if ( entName == "" )
        return NULL;

    if ( entName == "BaseEntity" )
        return new IEntity;

    if ( entName == "FloorSwitch" )
        return new FloorSwitch;

    if ( entName == "Door" )
        return new Door;

    if ( entName == "Shotgun" )
        return new Shotgun;

    if ( entName == "Melee" )
        return new Melee;

    //assert( static_cast<IEntity*>( ent ) && "Trying to create a class that is not an IEntity in EntityFactory" );
    //EntityManager::GetInstance()->RegisterEntity( static_cast<IEntity*>( ent ), entName );

    return NULL;
}
/** Create the Player */
Player*   EntityFactory::CreatePlayer(const std::string &pName)
{
    if ( !m_playerCreated )
    {
        m_player = new Player;
        m_playerCreated = true;
    }
    return m_player;
}


/** Create an entity */
IEntity* EntityFactory::CreatePrefabEntity( const std::string &ent_type )
{
    if ( m_entList.find( ent_type ) != m_entList.end() )
    {
        Chunk location = m_entList[ ent_type ];
        return ParseEntityFromFileChunk( location );
    }
    return NULL;
}
/** Import an entity from a file */
IEntity* EntityFactory::ParseEntityFromFileChunk( Chunk location )
{

/**------------------------------------------------------------------------------
        THIS IS TERRIBLE, WE ARE RUNNING REFLECTIVE COPIES OF THE SAME PIECE OF CODE
        WE MUST RETHINK/WORK HOW THIS WORKS
--------------------------------------------------------------------------------**/
    MFile file;
    IEntity* ent;

    // open stream
    if ( file.Open( location.fileName ) )
    {
        g_kernel->LPrintf( ("ERROR: Could not find: "+ location.fileName).c_str() );
        return NULL;
    }

    file.Seek( location.start );
    while( file.GetPosition() <= location.end && !file.IsEnd() )
    {

        // read the entities type name
        std::string entName = file.ReadNext(":");
        
        ent = NULL;
        if ( entName == "player" ) 
        {
            ent = CreatePlayer( entName );
        } 
        else
        {
            ent = CreateEntity( entName );

            if ( !ent )
            {
                g_kernel->LPrintf( ("**Error parsing Entities in: " 
                                            + location.fileName + " could not create entity, falling on default type!").c_str() );
                ent = CreateEntity( "BaseEntity" );
            }
        }

        ent->m_attributes.Put( "entity_type", entName );

        // read the entities reference
        std::string entRef  = file.ReadNext(":");
        std::string entityName  = file.ReadNext(":");
        
        /*--------------------------------------------------
            Start reading properties
        ---------------------------------------------------*/

        //
        file.ReadNext("{");
        file.ReadLine();

        bool rightBrace = false;
        while( !file.IsEnd() )
        {
            std::string line = file.ReadLine();

            if ( line == "}" ) 
            {
                rightBrace = true;
                break;
            }
            StringUtil util( line, ':' );
            
            std::string propName, v1, v2;
            // get the name
            if ( util.HasNext() )
            {
                propName = util.GetNext();
            }
            // get the value
            if ( util.HasNext() )
            {
                v1 = util.GetNext();
            }
            // get the 2nd value if applicable
            if ( util.HasNext() )
            {
                v2 = util.GetNext();
            }

            if ( v2 != "" && util.IsNumber( v1) && util.IsNumber( v2 ) )
            {
                ent->m_attributes.Put( propName, util.ToFloat( v1 ), util.ToFloat( v2 )  );     // vector 
            } else if ( util.IsNumber( v1 ) ){
                ent->m_attributes.Put( propName, util.ToInt( v1 ) ); // integer
            } else {
                ent->m_attributes.Put( propName, v1 );   // string
            }
        }

        if ( !rightBrace )
        {
            g_kernel->LPrintf( ("**Error parsing Entities in: " + location.fileName + " missing a closing \'}\' ").c_str() );
            g_kernel->LPrintf( "**On byte: %d of %d", file.GetPosition(), file.Size() );
            
            // EntityManager->GetInstance()->
            return NULL;
        }

        // apply common attributes
        ent->Apply();
        EntityManager::GetInstance()->PutInWorld( ent );
        EntityManager::GetInstance()->RegisterEntity( ent, entityName );
        /*--------------------------------------------------------
            Now parse out the states of these entity
        ---------------------------------------------------------*/
        
        // create the view
        EntityView* view = ViewManager::GetInstance()->CreateView( ent );
        view->SetVisible( true );

        file.ReadNext("{");
        file.ReadLine();
        std::string simage = file.ReadNext(":");
        
        int row = file.ReadInt();
        int col = file.ReadInt();

        Image image = g_kernel->GetResource()->LoadImages( simage, row, col );
        view->GetModel()->SetImage( image );

        bool rightBrace2 = false;
        // read states
        while( !file.IsEnd() )
        {
            if ( file.Peek() == '}' )
            {
                rightBrace2 = true;
                break;
            }
            // create our model directions
            ModelDirections* dirs = new ModelDirections;

            std::string state   = file.ReadNext(":");
            std::string sound   = file.ReadNext(":");
            std::string script  = file.ReadNext(":");

            // get all four directions
            int actionTime      = file.ReadInt();
            for ( int i = 0; i < 4; i++ ) 
            {
                int animTime        = file.ReadInt();
                int imageRow        = file.ReadInt();
                int imageCol        = file.ReadInt();
                int amount          = file.ReadInt();
                
                dirs->AddDirection( i, animTime, imageRow, imageCol, amount );
            }

            // add the state to the model
            view->GetModel()->AddState( state, dirs );
            
            // create the state
            State* entState = StateFactory::GetInstance()->CreateState( state );
            if ( sound != "null" && entState )
                entState->m_sound = g_kernel->GetResource()->LoadSound( sound );
            if ( script != "null" && entState )
                entState->m_scriptFile = script;

            entState->m_nextUpdate = actionTime;
            ent->AddState( state, entState );
            file.ReadLine();
        }

        if ( !rightBrace2 )
        {
            g_kernel->LPrintf( ("**Error parsing Entity Models in: " + location.fileName  + " missing a closing \'}\' ").c_str() );
            g_kernel->LPrintf( "**On byte: %d of %d", file.GetPosition(), file.Size() );
            return ent;
        }
        ent->SetState( ENT_STATES[IDLE] );

        file.ReadLine();
    }
    file.Close();
    return ent;
}

/** Clone an entity */
IEntity*  EntityFactory::CloneEntity(const IEntity* ent, const std::string &name)
{
    IEntity* new_ent = new IEntity( *ent );
    EntityManager::GetInstance()->RegisterEntity( new_ent, name );
    // for creating the view of the entity
    ViewManager* vMng = ViewManager::GetInstance();
    vMng->CreateView( new_ent );

    return new_ent;
}

/** Add to the list of types */
void EntityFactory::AddToPrefabList( const std::string &refName, Chunk location )
{
    // only add if we don't have a reference to this object
    if ( m_entList.find( refName ) == m_entList.end() )
        m_entList[ refName ] = location;
}